﻿using System;
using System.Linq;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Postnuclear.Collections;

namespace Postnuclear.Test.Helpers
  {
  /// <summary>
  /// Tests <see cref="INotifyCollectionChanged{T}"/> implementation behavior.
  /// </summary>
  public static class NotifyCollectionChangedValidator
    {
    #region Public

    // Methods

    /// <summary>
    /// Validates the specified collection.
    /// </summary>
    /// <typeparam name="T">Type of collection items.</typeparam>
    /// <param name="collectionCreator">The collection creator.</param>
    /// <param name="itemCreator">The item creator.</param>
    public static void Validate<T>(Func<IObservableCollection<T>> collectionCreator, Func<T> itemCreator)
      {
      IObservableCollection<T> collection = collectionCreator();
      T item = itemCreator();

      AddTest(collection, item);
      RemoveTest(collection, item);
      ClearTest(collectionCreator(), itemCreator(), itemCreator());
      }

    #endregion

    #region Private

    // Methods

    private static void AddTest<T>(IObservableCollection<T> collection, T item)
      {
      Assert.AreEqual(0, collection.Count);
      Assert.IsFalse(collection.Contains(item));

      int count = 0;
      int inpcCount = 0;

      collection.ItemAdded += (_, e) => Assert.AreEqual(item, e.Item);
      collection.ItemAdded += (_, e) => ++count;
      collection.PropertyChanged += (_, e) => inpcCount += "Count" == e.PropertyName ? 1 : 0;

      Assert.AreEqual(0, count);
      Assert.AreEqual(0, inpcCount);

      collection.Add(item);
      Assert.AreEqual(1, count);
      Assert.AreEqual(1, inpcCount);
      }

    private static void RemoveTest<T>(IObservableCollection<T> collection, T item)
      {
      Assert.AreEqual(1, collection.Count);
      Assert.IsTrue(collection.Contains(item));

      int count = 0;
      int inpcCount = 0;

      collection.ItemRemoved += (_, e) => Assert.AreEqual(item, e.Item);
      collection.ItemRemoved += (_, e) => ++count;
      collection.PropertyChanged += (_, e) => inpcCount += "Count" == e.PropertyName ? 1 : 0;

      Assert.AreEqual(0, count);
      Assert.AreEqual(0, inpcCount);

      collection.Remove(item);
      Assert.AreEqual(1, count);
      Assert.AreEqual(1, inpcCount);
      }

    private static void ClearTest<T>(IObservableCollection<T> collection, T first, T second)
      {
      Assert.AreEqual(0, collection.Count);
      Assert.IsFalse(collection.Contains(first));
      Assert.IsFalse(collection.Contains(second));

      collection.Add(first);
      collection.Add(second);
      int count = 0;
      int inpcCount = 0;

      collection.CollectionCleared += (_, e) => Assert.AreEqual(2, e.Value.Count());
      collection.CollectionCleared += (_, e) => Assert.AreEqual(first, e.Value.First());
      collection.CollectionCleared += (_, e) => Assert.AreEqual(second, e.Value.Last());
      collection.CollectionCleared += (_, e) => ++count;
      collection.PropertyChanged += (_, e) => inpcCount += "Count" == e.PropertyName ? 1 : 0;

      Assert.AreEqual(0, count);
      Assert.AreEqual(0, inpcCount);

      collection.Clear();
      Assert.AreEqual(1, count);
      Assert.AreEqual(1, inpcCount);
      }

    #endregion
    }
  }
